package com.mars.module.workflow.service.impl;

import com.google.common.collect.Maps;
import com.mars.common.base.UserContextInfo;
import com.mars.common.enums.ActionEnum;
import com.mars.common.util.BeanPropertyUtil;
import com.mars.framework.context.ContextUserInfoThreadHolder;
import com.mars.framework.exception.ServiceException;
import com.mars.module.workflow.entity.ActHiOpinion;
import com.mars.module.workflow.request.ApproveRequest;
import com.mars.module.workflow.request.StartRequest;
import com.mars.module.workflow.response.StartResponse;
import com.mars.module.workflow.service.IActHiOpinionService;
import com.mars.module.workflow.service.IProStartApproveService;
import lombok.AllArgsConstructor;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.FlowElement;
import org.activiti.bpmn.model.FlowNode;
import org.activiti.bpmn.model.StartEvent;
import org.activiti.engine.*;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.impl.RepositoryServiceImpl;
import org.activiti.engine.impl.persistence.entity.ExecutionEntity;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;

/**
 * 流程启动服务
 *
 * @author 程序员Mars
 */
@Service
@AllArgsConstructor
public class ProStartApproveServiceImpl implements IProStartApproveService {

    private final RepositoryService repositoryService;

    private final RuntimeService runtimeService;

    private final TaskService taskService;

    private final IdentityService identityService;

    private final HistoryService historyService;

    private final IActHiOpinionService actHiOpinionService;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public StartResponse startProcess(StartRequest request) {
        //流程发起前设置发起人，记录在流程历史中
        identityService.setAuthenticatedUserId(request.getApplyUser());
        request.getVariables().put(BeanPropertyUtil.getBeanFieldName(StartRequest::getApplyUser), request.getApplyUser());
        ProcessInstance instance;
        if (StringUtils.isEmpty(request.getTenantId())) {
            instance = runtimeService.startProcessInstanceByKey(request.getFlowKey(), request.getBusinessKey(), request.getVariables());
        } else {
            instance = runtimeService.startProcessInstanceByKeyAndTenantId(request.getFlowKey(), request.getBusinessKey(), request.getVariables(), request.getTenantId());
        }
        if (Objects.isNull(instance)) {
            throw new ServiceException("流程启动失败");
        }
        insertStartOpinion(instance, request.getApplyUser());
        //流程实例名称
        runtimeService.setProcessInstanceName(instance.getId(), request.getFlowName());
        StartResponse startResult = new StartResponse();
        startResult.setProcInstId(Long.valueOf(instance.getId()));
        List<Task> tasks = taskService.createTaskQuery().processInstanceId(instance.getId()).taskAssignee(request.getApplyUser()).list();
        if (!CollectionUtils.isEmpty(tasks) && tasks.size() == 1) {
            startResult.setTaskId(tasks.get(0).getId());
        }
        return startResult;
    }

    @Override
    public boolean submitApprove(ApproveRequest request) {
        UserContextInfo info = ContextUserInfoThreadHolder.get();
        String comment = "";
        if (Objects.nonNull(request.getOpinionType())) {
            identityService.setAuthenticatedUserId(info.getId().toString());
            comment = request.getOpinionType() == 1 ? "【同意】" + request.getOpinionContent() : "【拒绝】" + request.getOpinionContent();
            taskService.addComment(request.getTaskId(), request.getInstanceId(), comment);
        }
        if (MapUtils.isEmpty(request.getVariables())) {
            request.setVariables(Maps.newHashMap());
        }
        String taskId = request.getTaskId();
        // 被委派人处理完成任务
        // p.s. 被委托的流程需要先 resolved 这个任务再提交。
        // 所以在 complete 之前需要先 resolved
        // resolveTask() 要在 claim() 之前，不然 act_hi_taskinst 表的 assignee 字段会为 null
        taskService.resolveTask(request.getTaskId(), request.getVariables());
        // 只有签收任务，act_hi_taskinst 表的 assignee 字段才不为 null
        taskService.claim(taskId, info.getId().toString());
        taskService.complete(taskId, request.getVariables());
        Task task = taskService.createTaskQuery().processInstanceId(request.getInstanceId()).singleResult();
        return !Objects.isNull(task);
    }

    @Override
    public String getNextNodeId(String proInstanceId) {
        List<HistoricActivityInstance> activityList = historyService
                .createHistoricActivityInstanceQuery()
                .processInstanceId(proInstanceId)
                .unfinished()
                .list();
        // 获取下一个节点ID
        String nextNodeId = null;
        if (activityList.size() > 0) {
            HistoricActivityInstance currentActivity = activityList.get(0);
            // 获取当前节点的任务ID
            String currentTaskId = currentActivity.getTaskId();
            // 查询当前任务的后续任务
            List<Task> tasks = taskService.createTaskQuery()
                    .taskId(currentTaskId)
                    .list();

            if (tasks.size() > 0) {
                Task currentTask = tasks.get(0);
                // 获取下一个节点的任务
                List<Task> nextTasks = taskService.createTaskQuery()
                        .processInstanceId(proInstanceId)
                        .taskDefinitionKey(currentTask.getTaskDefinitionKey())
                        .list();
                if (nextTasks.size() > 0) {
                    Task nextTask = nextTasks.get(0);
                    nextNodeId = nextTask.getTaskDefinitionKey();
                }
            }
        }
        return nextNodeId;
    }


    /**
     * 插入流程启动日志
     *
     * @param pi             pi
     * @param processCreater processCreater
     */
    private void insertStartOpinion(ProcessInstance pi, String processCreater) {
        //根据流程定义id获取流程模型对象
        BpmnModel bpmnModel = repositoryService.getBpmnModel(pi.getProcessDefinitionId());
        //获得流程模型的所有元素（包含：开始节点 任务节点 结束节点 路由  服务节点 等等）
        Collection<FlowElement> flowElements = bpmnModel.getMainProcess().getFlowElements();
        //流程开始节点
        FlowNode targetNode = null;
        for (Iterator<FlowElement> it = flowElements.iterator(); it.hasNext(); ) {
            FlowElement O = it.next();
            //获取开始节点
            if (O instanceof StartEvent) {
                targetNode = (FlowNode) O;
                break;
            }
        }
        //插入审批日志
        actHiOpinionService.saveActHiOpinion(new ActHiOpinion(ActionEnum.A_START.getCode(), targetNode,
                pi.getId(), processCreater, "启动流程"));
    }

}
